﻿using OA.Infrastructure.Extensions;

namespace AMS.Services.Impl;

public class FreeAssetsConsumingService : IFreeAssetsConsumingService
{
    private readonly DbContext _context;

    private readonly IApprovalService _approvalService;

    public FreeAssetsConsumingService(DbContext context, IApprovalService approvalService)
    {
        _context = context;

        _approvalService = approvalService;
    }

    public async Task<PageResult<FreeAssetsConsuming.QueryModel>> GetPageAsync(FreeAssetsConsuming.ConditionSelectorCondition condition, CancellationToken cancellationToken)
    {
        var result = new PageResult<FreeAssetsConsuming.QueryModel>();

        var currentUser = _context.CurrentUser();

        var accessExpression = currentUser.Roles.Any(n => n.Name == "admin")
            ? (n => true)
            : (Expression<Func<FreeAssetsConsuming, bool>>?)(n => n.Consumer.Id == currentUser.Id);

        try
        {
            var expression = GetConditionSelectorCondition(condition);

            var data = await _context.Set<FreeAssetsConsuming>()
                .Include(m => m.Assets).ThenInclude(x => x.Category)
                .Include(m => m.Assets).ThenInclude(x => x.Supplier)
                .Include(m => m.Consumer)
                .Where(accessExpression)
                .Where(expression)
                .OrderByDescending(x => x.CreatedDate)
                .Skip((condition.PageNum - 1) * condition.PageSize)
                .Take(condition.PageSize)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = data.Select(x => GetQueryModel(x));

            result.PageNum = condition.PageNum;

            result.PageSize = condition.PageSize;

            result.Total = await _context.Set<FreeAssetsConsuming>().Where(accessExpression).CountAsync(expression, cancellationToken);

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<OperationalResult> CreateAsync(FreeAssetsConsuming.CreateModel freeAssetsConsuming, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        var currentUser = _context.CurrentUser();

        try
        {
            var entity = new FreeAssetsConsuming
            {
                Count = freeAssetsConsuming.Count,
                Remark = freeAssetsConsuming.Remark,
            };

            var assetsStorage = await _context.Set<FreeAssetsRepo>()
                .Include(n => n.Assets)
                .FirstOrDefaultAsync(n => n.Assets.Id == freeAssetsConsuming.AssetsId, cancellationToken);

            if (assetsStorage is not null)
            {
                entity.Assets = assetsStorage.Assets;
                entity.Consumer = currentUser;

                if (entity.Count > assetsStorage.Stock)
                {
                    result.Status = OperationalResult.FAILURE;
                    result.Message = "领用数量超过库存";

                    return result;
                }
            }

            await _context.Set<FreeAssetsConsuming>().AddAsync(entity, cancellationToken);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            if (result.Status == OperationalResult.SUCCESS)
            {
                _ = await _approvalService.CreateApprovalAsync(currentUser, entity.Id, ApprovalType.Consuming, AssetsType.Consumable, freeAssetsConsuming.Remark, ApprovalService.freeAssetsConsumingApprovalFlow, cancellationToken);
            }

            result.Message = $"{affectedRows} rows affected.";
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    private static FreeAssetsConsuming.QueryModel GetQueryModel(FreeAssetsConsuming freeAssetsConsuming)
    {
        return new FreeAssetsConsuming.QueryModel
        {
            Id = freeAssetsConsuming.Id,
            CreatedDate = freeAssetsConsuming.CreatedDate,
            LastModifiedDate = freeAssetsConsuming.LastModifiedDate,
            Assets = FreeAssetsService.GetQueryModel(freeAssetsConsuming.Assets),
            Count = freeAssetsConsuming.Count,
            Remark = freeAssetsConsuming.Remark,
            Consumer = DefinitionServiceExtensions.GetUserQueryModel(freeAssetsConsuming.Consumer),
            Status = freeAssetsConsuming.Status,
        };
    }

    private static Expression<Func<FreeAssetsConsuming, bool>> GetConditionSelectorCondition(FreeAssetsConsuming.ConditionSelectorCondition condition)
    {
        Expression<Func<FreeAssetsConsuming, bool>>? expression = null;

        if (condition.Keywords is not null)
        {
            Expression<Func<FreeAssetsConsuming, bool>>? keywords = null;
            keywords = freeAssetsConsuming => freeAssetsConsuming.Assets.Name.Contains(condition.Keywords);
            keywords = keywords.Or(freeAssetsConsuming => freeAssetsConsuming.Assets.Supplier.Name.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsConsuming => freeAssetsConsuming.Assets.Batch.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsConsuming => freeAssetsConsuming.Consumer.JobNumber.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsConsuming => freeAssetsConsuming.Consumer.Name.Contains(condition.Keywords));
            keywords = keywords.Or(freeAssetsConsuming => freeAssetsConsuming.Consumer.Department.Name.Contains(condition.Keywords));
            expression = expression?.And(keywords) ?? keywords;
        }

        if (condition.StatusSelector is not null)
        {
            Expression<Func<FreeAssetsConsuming, bool>>? statusSelector = null;
            statusSelector = freeAssetsConsuming => condition.StatusSelector == freeAssetsConsuming.Status;
            expression = expression?.And(statusSelector) ?? statusSelector;
        }

        return expression ?? (freeAssetsConsuming => true);
    }
}
